using System;
using System.Collections;
using System.Data;
using System.IO;
using System.Net.Mail;
using System.Text;
using System.Web;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using PHSRAG.Insight21.CDS;
using PHSRAG.Insight21.Common.Pages;
using PHSRAG.Insight21.Context;
using PHSRAG.Insight21.Policy;
using PHSRAG.Utility;

namespace PHSRAG.Insight21.COI
{
    /// <summary>
    /// Allows the superuser to send notifications/reminders to users
    /// </summary>
    public partial class CommunicationManagement : BasePage
    {
        #region Constants

        private const string COIAdminConfigKey = "Email.COI.Admin";
        private const string COIAdminToEmailConfigKey = "Email.COI.Admin.ToEmail";
        private const string MaxEmailsPerRelayConfigKey = "MaxEmailsPerRelay";
        private const string PageCacheKey = "pageCacheKey_5E4D70C1-BAC1-437e-9E6E-D7B5D2746B5A";
        private const string ScanAttachmentsForVirusesConfigKey = "ScanAttachmentsForViruses";

        #endregion

        #region Instance Variables (Private)

        private readonly string[] filePaths = new string[3];
        private ICachePolicy applicationCachePolicy;
        private ICachePolicy cachePolicy;

        #endregion

        #region Page event handlers

        /// <summary>
        /// Loads notification types drop down with values from refcodes
        /// </summary>
        /// <param name="sender">The object that raised the event</param>
        /// <param name="e">Argument associated with the event</param>
        protected void OnPageLoad(object sender, EventArgs e)
        {
            try
            {
                annunciator.Message = string.Empty;

                breadCrumb.Add("Communication Management", Settings.
                    GetKeyValue("SubNavigation.CoI.CommunicationMangementURL", string.Empty),
                    Settings.GetKeyValue("BreadCrumb.SummaryPageLevel", 1));

                if (!IsPostBack)
                {
                    LoadReportingPeriods();

                    RefCodes refCodes = (RefCodes) applicationCachePolicy.GetCachedObject(RefCodes.CacheKey);

                    ArrayList notificationTypesRefCodes = refCodes.GetSortedRefCodes(Constants.NotificationTypes.Domain, Constants.NotificationTypes.AttributeOrder);

                    if (notificationTypesRefCodes.Count > 0)
                    {
                        notificationType.Items.Add(new ListItem("-- Select Type --", "0"));
                    }
                    foreach (RefCode refCode in notificationTypesRefCodes)
                    {
                        notificationType.Items.Add(new ListItem(refCode.Meaning, refCode.Code));
                    }
                    PopulateInstitution();
                }
            }
            catch (Exception ex)
            {
                DisplayExceptionReport(ex);
            }
        }

        #endregion

        #region Web Form Designer generated code

        /// <summary>
        /// Page Initialization
        /// </summary>
        /// <param name="e">Argument associated with the event</param>
        protected override void OnInit(EventArgs e)
        {
            //
            // CODEGEN: This call is required by the ASP.NET Web Form Designer.
            //
            InitializeComponent();
            base.OnInit(e);
            cachePolicy = (Policy.ICachePolicy) Session[Policy.Names.CachePolicyKey];
            applicationCachePolicy = (Policy.ICachePolicy) Application[Policy.Names.ApplicationCachePolicyKey];
        }


        /// <summary>
        /// Required method for Designer support - do not modify
        /// the contents of this method with the code editor.
        /// </summary>
        private void InitializeComponent()
        {
            this.header.Navigate += new System.Web.UI.WebControls.CommandEventHandler(this.OnNavigate);
            this.breadCrumb.Navigate += new System.Web.UI.WebControls.CommandEventHandler(this.OnBreadCrumbNavigate);
            this.saveContent.Click += new ImageClickEventHandler(OnSaveContent);
            this.sendMail.Click += new ImageClickEventHandler(OnSendMail);
            this.Load += new System.EventHandler(this.OnPageLoad);
        }

        #endregion

        #region Private methods

        /// <summary>
        /// Saves the file uploaded to the temporary folder and scans it
        /// </summary>
        /// <returns>true - if no file is selected by the user, or if the file selected is uploaded without any virus issues</returns>
        private bool ProcessUploadedFiles()
        {
            bool retValue = true;
            for (int i = 0; i < 3; i++)
            {
                HtmlInputFile file = (HtmlInputFile) FindControl("attachment" + i);
                HttpPostedFile postedFile = file.PostedFile;
                if (postedFile != null && postedFile.FileName.Length > 0)
                {
                    try
                    {
                        string fileName = Path.GetFileName(postedFile.FileName);
                        if (postedFile.ContentLength > 0)
                        {
                            //store uploaded files in a temporary location, virus scan
                            filePaths[i] = Path.Combine(Path.GetTempPath(), fileName);

                            postedFile.SaveAs(filePaths[i]);

                            if (bool.Parse(Settings.GetKeyValue(ScanAttachmentsForVirusesConfigKey, "false")))
                            {
                                if (!Document.ScanFile(filePaths[i]))
                                {
                                    File.Delete(filePaths[i]);
                                    annunciator.Title = InsightUtilities.Constants.Annunciator.Title;
                                    annunciator.Message = fileName + " is infected.";
                                    retValue = false;
                                }
                            }
                        }
                        else
                        {
                            annunciator.Title = InsightUtilities.Constants.Annunciator.Title;
                            annunciator.Message = fileName + " is empty or invalid.";
                            retValue = false;
                        }
                    }
                    catch (Exception ex)
                    {
                        ExceptionReport.WriteToEventLog("CommunicationManagement.ProcessUploadedFile(): Failed to process the uploaded file",
                            ex, true, ExceptionReport.ReportType.Error);
                        annunciator.Title = InsightUtilities.Constants.Annunciator.Title;
                        annunciator.Message = "Failed to process the uploaded file";
                        retValue = false;
                    }
                }
            }
            return retValue;
        }

        /// <summary>
        /// Displays an error message on the screen to the user with the exception, and all
        /// inner exception messages.
        /// </summary>
        /// <param name="ex">The exception to report on</param>
        private void DisplayExceptionReport(Exception ex)
        {
            annunciator.Title = InsightUtilities.Constants.Annunciator.Title;
            annunciator.Message = ExceptionReport.GetExceptionInfo(ex, false);
        }

        /// <summary>
        /// Populate the institution dropdown for partial notification(notifying only for the selected institution).
        /// </summary>
        private void PopulateInstitution()
        {
            try
            {
                DataTable instituionTable = COIUserSummary.GetInstitutionOrUnitOrDepartment(DataAccess.ConnectionString, 0, 0);
                DataRow dr = instituionTable.NewRow();
                dr["InstitutionId"] = 0;
                dr["Name"] = "-- Select Institution --";
                instituionTable.Rows.InsertAt(dr, 0);
                institution.DataSource = instituionTable;
                institution.DataTextField = "Name";
                institution.DataValueField = "InstitutionId";
                institution.DataBind();
            }
            catch (Exception ex)
            {
                ExceptionReport.WriteToEventLog("CommunicationManagement.PopulateInstitution(): Failed to populate the Institution",
                    ex, true, ExceptionReport.ReportType.Error);
                annunciator.Title = InsightUtilities.Constants.Annunciator.Title;
                annunciator.Message = "Failed to Populate the Institution";
            }
        }

        private void ToggleReportingPeriodEntryControls()
        {
            if (chkReportingPeriod.Checked)
            {
                txtReportingPeriod.Visible = true;
                ddlReportingPeriod.Visible = false;
            }
            else
            {
                txtReportingPeriod.Visible = false;
                ddlReportingPeriod.Visible = true;
            }
        }

        private void LoadReportingPeriods()
        {
            ddlReportingPeriod.DataSource = COICollection.GetReportingPeriods(cachePolicy, DataAccess.ConnectionString, true);
            ddlReportingPeriod.DataTextField = "ReportingPeriod";
            ddlReportingPeriod.DataValueField = "Id";
            ddlReportingPeriod.DataBind();
            ToggleReportingPeriodEntryControls();
        }

        #endregion

        #region Control event handlers

        /// <summary>
        /// Loads the COIEmailTemplate object based on the notification type selected
        /// and displays the corresponding body text on the UI
        /// </summary>
        /// <param name="sender">The object raising an event.</param>
        /// <param name="e">Argument associated with the event</param>
        protected void OnNotificationTypeChanged(object sender, EventArgs e)
        {
            try
            {
                institution.SelectedIndex = 0;
                lastCommunicationDate.Reset();
                if (notificationType.SelectedIndex > 0)
                {
                    if (notificationType.SelectedValue == Constants.NotificationTypes.OpenEnrollment)
                    {
                        chkReportingPeriod.Enabled = true;
                        ddlReportingPeriod.Enabled = true;
                        txtReportingPeriod.Enabled = true;
                    }
                    else
                    {
                        chkReportingPeriod.Enabled = false;
                        ddlReportingPeriod.Enabled = false;
                        txtReportingPeriod.Enabled = false;
                    }
                    COIEMailTemplate mailTemplate = new COIEMailTemplate(cachePolicy);
                    mailTemplate.Load(DataAccess.ConnectionString, notificationType.SelectedValue);
                    emailBody.Text = mailTemplate.BodyText;
                    string mailSubject = mailTemplate.Subject;
                    emailSubject.Text = (mailSubject != string.Empty) ? mailSubject : notificationType.SelectedItem.Text;
                    RefCodes refCodes = (RefCodes) applicationCachePolicy.GetCachedObject(RefCodes.CacheKey);
                    sendMail.Visible = Convert.ToBoolean(refCodes.GetRefCode(Constants.NotificationTypes.Domain, notificationType.SelectedValue).Attributes[Constants.NotificationTypes.AttributeCanSendEmail]);

                    lastCommunicationDate.Visible = activityDate.Visible = (notificationType.SelectedValue == Constants.NotificationTypes.OpenEnrollment) ? true : false;
                    institution.Visible = institutionCaption.Visible = sendMail.Visible && (notificationType.SelectedValue == Constants.NotificationTypes.Individual) ? false : true;
                    emailSubjectCaption.Visible = emailSubject.Visible = true;
                    emailSubjectCaption.Text = "Email Subject:";
                    invalidEmailList.Text = string.Empty;
                }
                else
                {
                    emailBody.Text = emailSubject.Text = string.Empty;
                    institution.Visible = institutionCaption.Visible = false;
                }
            }
            catch (Exception ex)
            {
                DisplayExceptionReport(ex);
            }
        }

        /// <summary>
        /// Saves the email template to the database
        /// </summary>
        /// <param name="sender">The object raising an event.</param>
        /// <param name="e">Argument associated with the event</param>
        private void OnSaveContent(object sender, ImageClickEventArgs e)
        {
            try
            {
                if (notificationType.SelectedIndex > 0)
                {
                    COIEMailTemplate mailTemplate = new COIEMailTemplate(cachePolicy);
                    mailTemplate.Type = notificationType.SelectedValue;
                    mailTemplate.BodyText = emailBody.Text.Trim();
                    mailTemplate.Subject = emailSubject.Text.Trim();
                    if (chkReportingPeriod.Checked)
                    {
                        //insert reporting period and update dropdown
                        COICollection.AddReportingPeriod(DataAccess.ConnectionString, txtReportingPeriod.Text.Trim());
                    }
                    mailTemplate.Save(DataAccess.ConnectionString);
                    chkReportingPeriod.Checked = false;
                    LoadReportingPeriods();
                    if (!Page.ClientScript.IsStartupScriptRegistered("TemplateSaved"))
                    {
                        Page.ClientScript.RegisterStartupScript(GetType(), "TemplateSaved", "<script language=\"javascript\">alert('Mail content saved to the database.');</script>");
                    }
                }
                else
                {
                    annunciator.Title = InsightUtilities.Constants.Annunciator.Title;
                    annunciator.Message = "Select the type of notification";
                }
            }
            catch (Exception ex)
            {
                DisplayExceptionReport(ex);
            }
        }

        /// <summary>
        /// 1. Stores the attachment, if any, in a temporary folder, scans it.
        /// 
        /// 2. 
        ///	a. Open Enrollment: Adds an Start COI to all the users who have access to COI and who do not have
        ///		a Start or an In Progress COI. For users with existing Start/InProgress COI, the last notified
        ///		date is updated. 
        ///	b. Reminder/Overdue: Updates the last notified date for all existing Start/InProgress COIs 
        /// 
        /// 3. Sends email with the attachment to users for whom new COIs have been added or whose COIs have been updated 
        /// </summary>
        /// <param name="sender">The object that raised the event</param>
        /// <param name="e">Argument associated with the event</param>
        private void OnSendMail(object sender, ImageClickEventArgs e)
        {
            try
            {
                invalidEmailList.Text = string.Empty;
                if (notificationType.SelectedIndex <= 0)
                {
                    annunciator.Title = InsightUtilities.Constants.Annunciator.Title;
                    annunciator.Message = "Please select the type of notification";
                    return;
                }

                if (notificationType.SelectedItem.Value == Constants.NotificationTypes.OpenEnrollment &&
                    ((chkReportingPeriod.Checked && txtReportingPeriod.Text.Trim() == string.Empty) ||
                        (!chkReportingPeriod.Checked && ddlReportingPeriod.SelectedIndex == 0)))
                {
                    annunciator.Title = InsightUtilities.Constants.Annunciator.Title;
                    annunciator.Message = "Please specify a reporting period";
                    return;
                }
                int id = 0;
                if (chkReportingPeriod.Checked)
                {
                    //insert reporting period and update dropdown
                    id = COICollection.AddReportingPeriod(DataAccess.ConnectionString, txtReportingPeriod.Text.Trim());
                }
                DataSet dsNotification = COIUserSummary.ExecuteNotification(DataAccess.ConnectionString,
                    notificationType.SelectedValue,
                    Convert.ToInt32(institution.SelectedValue),
                    (lastCommunicationDate.Selection.Trim() == string.Empty) ? DateTime.MinValue : Convert.ToDateTime(lastCommunicationDate.Selection.Trim()),
                    (chkReportingPeriod.Checked ? id : Convert.ToInt32(ddlReportingPeriod.SelectedValue)));

                if (dsNotification != null)
                {
                    /*Always the datatable will be returned for the Valid and Invalid email List 
					 Since the select statement will alsways be executed*/
                    if (dsNotification.Tables["InvalidEmaillist"].Rows.Count > 0)
                    {
                        invalidEmailList.Visible = true;

                        StringBuilder invalidEmail = new StringBuilder();
                        foreach (DataRow dr in dsNotification.Tables["InvalidEmaillist"].Rows)
                        {
                            if (notificationType.SelectedValue == Constants.NotificationTypes.OpenEnrollment
                                && Convert.ToBoolean(dr[(int) NotifyUser.AddCOI]))
                            {
                                /// Call COICollection.AddCOI for each row
                                COICollection.AddNewCOI(cachePolicy, Convert.ToInt32(dr[(int) NotifyUser.AdmPersonId]), Constants.COIDateType.None,Constants.COIType.AnnualCycle,Constants.NotificationTypes.OpenEnrollment);
                            }
                            invalidEmail.Append(string.Format("{0} with Email: {1}, ", dr[(int) NotifyUser.FullName], dr[(int) NotifyUser.EmailAddress]));
                        }
                        invalidEmailList.Text = "--- Following are the invalid Email addresses " + invalidEmail + " ---";
                        invalidEmailList.Visible = invalidEmailCaption.Visible = true;
                    }

                    if (dsNotification.Tables["ValidEmailList"].Rows.Count > 0)
                    {
                        // Save and scan posted attachments
                        if (!ProcessUploadedFiles())
                        {
                            return;
                        }

                        int maxEmailLimitPerRelay = Settings.GetKeyValue(MaxEmailsPerRelayConfigKey, 0);
                        if (maxEmailLimitPerRelay == 0)
                        {
                            if (!Page.ClientScript.IsStartupScriptRegistered("MaxEmailRelayException"))
                            {
                                Page.ClientScript.RegisterStartupScript(GetType(), "MaxEmailRelayException", "<script language=\"javascript\">alert('The number of emails per relay is not set or set to zero(0). Please correct this and try again.');</script>");
                                return;
                            }
                        }
                        StringBuilder emailList = new StringBuilder();

                        int i = 0;
                        foreach (DataRow dr in dsNotification.Tables["ValidEmailList"].Rows)
                        {
                            if (notificationType.SelectedValue == Constants.NotificationTypes.OpenEnrollment
                                && Convert.ToBoolean(dr[(int) NotifyUser.AddCOI]))
                            {
                                /// Call COICollection.AddCOI for each row
                                COICollection.AddNewCOI(cachePolicy, Convert.ToInt32(dr[(int) NotifyUser.AdmPersonId]), Constants.COIDateType.None,Constants.COIType.AnnualCycle,Constants.NotificationTypes.OpenEnrollment);
                            }
                            emailList.Append(string.Format("{1}{0}",
                                dr[(int) NotifyUser.EmailAddress],
                                emailList.Length == 0 ? "" : ", "));

                            if ((i + 1)%maxEmailLimitPerRelay == 0)
                            {
                                if (!SendMail(emailList.ToString(), maxEmailLimitPerRelay))
                                {
                                    invalidEmailList.Text = invalidEmailList.Text.Trim() + " --- Exception occured while trying to send the following emails. Please verify the following email addresses: " + emailList + " ---";
                                    invalidEmailList.Visible = invalidEmailCaption.Visible = true;
                                }
                                emailList = new StringBuilder(string.Empty);
                            }
                            ++i;
                        }
                        if (emailList.ToString() != string.Empty)
                        {
                            if (!SendMail(emailList.ToString(), maxEmailLimitPerRelay))
                            {
                                invalidEmailList.Text = invalidEmailList.Text.Trim() + " --- Exception occured while trying to send the following emails. Please verify the following email addresses: " + emailList + " ---";
                                invalidEmailList.Visible = invalidEmailCaption.Visible = true;
                            }
                        }
                        if (!Page.ClientScript.IsStartupScriptRegistered("NotificationComplete"))
                        {
                            Page.ClientScript.RegisterStartupScript(GetType(), "NotificationComplete", "<script language=\"javascript\">alert('All users successfully notified.');</script>");
                        }
                    }
                }
                else
                {
                    annunciator.Message = "There are no COI mail recipients that meet the selection criteria.";
                }

                chkReportingPeriod.Checked = false;
                LoadReportingPeriods();
            }
            catch (Exception ex)
            {
                DisplayExceptionReport(ex);
            }
            finally
            {
                for (int i = 0; i < 3; i++)
                {
                    if (!string.IsNullOrEmpty(filePaths[i]))
                    {
                        File.Delete(filePaths[i]);
                    }
                }
            }
        }

        /// <summary>
        /// This private method will perform the actual mail relay.
        /// If maxEmailPerRelay = 1 then User email Id will be added in the to address
        /// If maxEmailPerRelay > 1 then User Email Id will be added to the BCC
        /// </summary>
        /// <param name="emailList">Semicolon delimitted email Ids to which the email has to be relayed</param>
        /// <param name="maxEmailPerRelay">Email etting in the config file </param>
        /// <returns>returns a boolean value denoting the email relay(success or failure)</returns>
        private bool SendMail(string emailList, int maxEmailPerRelay)
        {
            MailMessage message = new MailMessage();
            bool isSendMailSuccess = true;
            try
            {
                if (string.IsNullOrEmpty(emailList))
                {
                    throw new ArgumentNullException("emailList cannot be null or empty.");
                }

                UserContext userContext = cachePolicy.GetCachedObject(UserContext.CacheKey) as UserContext;

                message.From = new MailAddress(Settings.GetKeyValue(COIAdminConfigKey, string.Empty));
                message.To.Add((maxEmailPerRelay == 1) ? emailList : Settings.GetKeyValue(COIAdminToEmailConfigKey, string.Empty));
                if (maxEmailPerRelay != 1)
                {
                    message.Bcc.Add(emailList);
                }
                message.Subject = emailSubject.Text.Trim();
                message.Body = emailBody.Text.Trim();
                message.IsBodyHtml = true;

                for (int i = 0; i < 3; i++)
                {
                    if (!string.IsNullOrEmpty(filePaths[i]))
                    {
                        message.Attachments.Add(new Attachment(filePaths[i]));
                    }
                }

                SmtpClient client = new SmtpClient(Settings.GetKeyValue("Email.SMTPServer", "127.0.0.1"));
                client.UseDefaultCredentials = true;
                client.Send(message);
            }
            catch
            {
                isSendMailSuccess = false;
            }
            finally
            {
                message.Dispose();
            }
            return isSendMailSuccess;
        }

        /// <summary>
        /// Resets the subnavigation selected link(s) before the 
        /// header performs a redirect.
        /// </summary>
        /// <param name="sender">Control which rasied the event.</param>
        /// <param name="e">Arguments associated with the event.</param>
        private void OnNavigate(object sender, CommandEventArgs e)
        {
            try
            {
                coiAdminSubNavigation.Reset();
                coiSubNavigation.Reset();
            }
            catch (Exception ex)
            {
                DisplayExceptionReport(ex);
            }
        }

        /// <summary>
        /// Removes COICollection from cache before redirection
        /// </summary>
        /// <param name="sender">Control which rasied the event.</param>
        /// <param name="e">Arguments associated with the event.</param>
        private void OnBreadCrumbNavigate(object sender, CommandEventArgs e)
        {
            try
            {
                coiAdminSubNavigation.Reset();
            }
            catch (Exception ex)
            {
                DisplayExceptionReport(ex);
            }
        }

        protected void OnChkReportingPeriod_CheckedChanged(object sender, EventArgs e)
        {
            ToggleReportingPeriodEntryControls();
        }

        #endregion

        #region Enum

        private enum NotifyUser
        {
            EmailAddress = 0,
            FullName,
            AdmPersonId,
            AddCOI
        }

        #endregion
    }
}